package org.hbhk.hxl.gateway.services;

import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import org.hbhk.hxl.gateway.model.CacheUrl;
import org.hbhk.hxl.gateway.model.Circuitbreaker;
import org.hbhk.hxl.gateway.model.HttpNode;
import org.hbhk.hxl.gateway.model.HttpService;
import org.hbhk.hxl.gateway.model.Isolation;
import org.hbhk.hxl.gateway.model.RateLimit;
import org.hbhk.hxl.gateway.model.ServiceNode;
import org.hbhk.hxl.gateway.redis.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.util.concurrent.RateLimiter;

@Component("hmsRedisService")
public class HmsRedisService implements IGatewayService {

	@Autowired
	private JdbcTemplate jdbcTemplate;

	@Value("${gateway.redis.expire:5}")
	private Integer expire = 5;

	@Override
	public HttpService getHttpService(String url) {
		String key = "http_" + url;
		HttpService service = RedisUtil.get(HttpService.class, key);
		if (service != null) {
			return service;
		}
		service = loadServices(url);
		if (service == null) {
			return null;
		}
		RedisUtil.put(key, service, expire, TimeUnit.MINUTES);
		return service;
	}

	@Override
	public List<String> getNode(String app) {

		String key = "node_" + app;

		List<String> nodes = RedisUtil.get(List.class, key);
		if (nodes != null) {
			return nodes;
		}
		nodes = loadNodes(app);

		if (nodes == null || nodes.isEmpty()) {
			return null;
		}

		RedisUtil.put(key, nodes, expire, TimeUnit.MINUTES);

		return nodes;

	}

	@Override
	public List<String> getGrayNode(String app) {

		String key = "graynode_" + app;

		List<String> nodes = RedisUtil.get(List.class, key);
		if (nodes != null) {
			return nodes;
		}
		nodes = loadGrayNodes(app);
		if (nodes == null || nodes.isEmpty()) {
			return null;
		}

		RedisUtil.put(key, nodes, expire, TimeUnit.MINUTES);

		return nodes;
	}

	@Override
	public String getApp(String url) {
		HttpService httpService = getHttpService(url);
		if (httpService == null) {
			return null;
		}
		return httpService.getAppName();
	}

	@Override
	public Integer getCacheUrl(String url) {

		String key = "cacheUrl_" + url;

		Integer time = RedisUtil.get(Integer.class, key);
		if (time != null) {
			return time;
		}
		time = loadCacheUrl(url);
		if (time == null) {
			return null;
		}

		RedisUtil.put(key, time, expire, TimeUnit.MINUTES);

		return loadCacheUrl(url);
	}

	@Override
	public List<String> getDisableNode(String url) {

		String key = "disableNode_" + url;

		List<String> nodes = RedisUtil.get(List.class, key);
		if (nodes != null) {
			return nodes;
		}
		nodes = loadDisableNodes(url);
		if (nodes == null || nodes.isEmpty()) {
			return null;
		}

		RedisUtil.put(key, nodes, expire, TimeUnit.MINUTES);

		return nodes;

	}

	@Override
	public Circuitbreaker getBreaker(String url) {

		String key = "breaker_" + url;
		Circuitbreaker service = RedisUtil.get(Circuitbreaker.class, key);
		if (service != null) {
			return service;
		}
		service = loadBreaker(url);
		if (service == null) {
			return null;
		}
		RedisUtil.put(key, service, expire, TimeUnit.MINUTES);
		return service;

	}

	@Override
	public Isolation getIsolation(String url) {

		String key = "isolation_" + url;
		Isolation service = RedisUtil.get(Isolation.class, key);
		if (service != null) {
			return service;
		}
		service = loadIsolation(url);
		if (service == null) {
			return null;
		}
		RedisUtil.put(key, service, expire, TimeUnit.MINUTES);
		return service;
	}

	private Map<String, RateLimiter> rateLimitCache = Maps.newConcurrentMap();

	@Override
	public RateLimiter getRateLimit(String url) {

		String key = "rateLimiter_" + url;
		RateLimit service = RedisUtil.get(RateLimit.class, key);
		if (service != null) {
			if (rateLimitCache.containsKey(key)) {
				return rateLimitCache.get(key);
			}
			Integer count = service.getCounter();
			Integer timer = service.getTime();
			if (timer == null || timer == 0) {
				timer = 60;
			}
			double r = Double.parseDouble(count + "") / timer;
			RateLimiter rateLimiter = RateLimiter.create(r);
			rateLimitCache.put(key, rateLimiter);
			return rateLimiter;
		}
		rateLimitCache.remove(key);

		service = loadRatelimit(url);
		if (service == null) {
			return null;
		}
		Integer count = service.getCounter();
		Integer timer = service.getTime();
		if (timer == null || timer == 0) {
			timer = 60;
		}
		double r = Double.parseDouble(count + "") / timer;
		RateLimiter rateLimiter = RateLimiter.create(r);

		rateLimitCache.put(key, rateLimiter);

		RedisUtil.put(key, service, expire, TimeUnit.MINUTES);
		return rateLimiter;

	}

	@Override
	public void reset(String msg) {

	}

	private HttpService loadServices(String url) {

		String contextPath = url.split("/")[1];
		String sql = "select context_path,url, enable,timeout,info,app_name from hms_gateway_service where context_path = ?";

		List<Map<String, Object>> allMap = jdbcTemplate.queryForList(sql, new Object[] { contextPath });
		if (allMap == null || allMap.isEmpty()) {
			sql = "select context_path,url, enable,timeout,info,app_name from hms_gateway_service where url = ?";
			allMap = jdbcTemplate.queryForList(sql, new Object[] { url });
		}

		if (allMap == null || allMap.isEmpty()) {

			return null;
		}
		List<HttpService> httpServices = convert(allMap, HttpService.class);

		return httpServices.get(0);

	}

	private List<String> loadNodes(String app) {
		String sql = "select  app,node, enable,gray from hms_gateway_node  where app= ? and   enable = '1'";

		List<Map<String, Object>> allMap = jdbcTemplate.queryForList(sql, new Object[] { app });

		if (allMap == null || allMap.isEmpty()) {
			return null;
		}

		List<HttpNode> httpServices = convert(allMap, HttpNode.class);
		List<String> nodes = Lists.newArrayList();

		for (HttpNode httpNode : httpServices) {
			nodes.add(httpNode.getNode());
		}
		return nodes;

	}

	private List<String> loadGrayNodes(String app) {
		String sql = "select  app,node, enable,gray from hms_gateway_node  where app= ? and   enable = '1' and gray='1'";

		List<Map<String, Object>> allMap = jdbcTemplate.queryForList(sql, new Object[] { app });

		if (allMap == null || allMap.isEmpty()) {
			return null;
		}

		List<HttpNode> httpServices = convert(allMap, HttpNode.class);
		List<String> nodes = Lists.newArrayList();

		for (HttpNode httpNode : httpServices) {
			nodes.add(httpNode.getNode());
		}
		return nodes;

	}

	private List<String> loadDisableNodes(String url) {
		String sql = "select  url,node, enable from hms_gateway_service_node  where   url= ? and  enable= '0'";

		List<Map<String, Object>> allMap = jdbcTemplate.queryForList(sql, new Object[] { url });
		if (allMap == null || allMap.isEmpty()) {
			return null;
		}

		List<ServiceNode> httpServices = convert(allMap, ServiceNode.class);

		List<String> nodes = Lists.newArrayList();

		for (ServiceNode serviceNode : httpServices) {
			nodes.add(serviceNode.getNode());
		}
		return nodes;

	}

	private RateLimit loadRatelimit(String url) {
		String sql = "select r.time,r.counter,s.interface_code "
				+ "from hms_gateway_ratelimit r,hms_gateway_service_config s "
				+ "where s.type = 'ratelimit'  and r.group_code = s.group_code and r.enable='1' and  s.interface_code=?";

		List<Map<String, Object>> list = jdbcTemplate.queryForList(sql, new Object[] { url });

		if (list == null || list.isEmpty()) {
			return null;
		}
		List<RateLimit> convertList = convert(list, RateLimit.class);

		return convertList.get(0);

	}

	private Isolation loadIsolation(String url) {
		String sql = "select r.time,r.counter,s.interface_code "
				+ "from hms_gateway_isolation r,hms_gateway_service_config s "
				+ "where s.type = 'isolation'  and r.group_code = s.group_code and r.enable='1' and  s.interface_code=?";

		List<Map<String, Object>> list = jdbcTemplate.queryForList(sql, new Object[] { url });

		if (list == null || list.isEmpty()) {
			return null;
		}
		List<Isolation> convertList = convert(list, Isolation.class);
		return convertList.get(0);

	}

	private Circuitbreaker loadBreaker(String url) {
		String sql = "select r.timeout,r.success_threshold,r.failure_threshold,s.interface_code "
				+ "from hms_gateway_breaker r,hms_gateway_service_config s "
				+ "where s.type = 'breaker'  and r.group_code = s.group_code and r.enable='1' and  s.interface_code=?";

		List<Map<String, Object>> list = jdbcTemplate.queryForList(sql, new Object[] { url });

		if (list == null || list.isEmpty()) {
			return null;
		}
		List<Circuitbreaker> convertList = convert(list, Circuitbreaker.class);

		return convertList.get(0);

	}

	private Integer loadCacheUrl(String url) {
		String sql = "select r.time,s.interface_code " + "from hms_gateway_cache r,hms_gateway_service_config s "
				+ "where s.type = 'cacheUrl'  and r.group_code = s.group_code and r.enable='1' and  s.interface_code=?";

		List<Map<String, Object>> list = jdbcTemplate.queryForList(sql, new Object[] { url });

		if (list == null || list.isEmpty()) {
			return null;
		}
		List<CacheUrl> convertList = convert(list, CacheUrl.class);

		return convertList.get(0).getTime();

	}

	public JdbcTemplate getJdbcTemplate() {
		return jdbcTemplate;
	}

	public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
		this.jdbcTemplate = jdbcTemplate;
	}

	@SuppressWarnings("unchecked")
	private <T> List<T> convert(List<Map<String, Object>> list, Class<T> cls) {
		List<T> objs = Lists.newArrayList();
		try {
			Field[] fields = cls.getDeclaredFields();
			Map<String, Field> fs = Maps.newConcurrentMap();
			for (Field field : fields) {
				fs.put(field.getName(), field);
			}
			for (Map<String, Object> map : list) {
				Set<String> keys = map.keySet();
				Object obj = cls.newInstance();
				for (String key : keys) {
					String f = key;
					if (key.indexOf("_") > -1) {
						String[] ar = key.split("_");
						StringBuffer sb = new StringBuffer();
						for (int i = 0; i < ar.length; i++) {
							if (i == 0) {
								sb.append(ar[i]);
							} else {
								sb.append(ar[i].substring(0, 1).toUpperCase() + ar[i].substring(1, ar[i].length()));
							}
						}
						f = sb.toString();
					}
					if (!fs.containsKey(f)) {
						continue;
					}
					fs.get(f).setAccessible(true);
					fs.get(f).set(obj, map.get(key));
				}
				objs.add((T) obj);
			}

		} catch (Exception e) {
			e.printStackTrace();
		}
		return objs;

	}

}
